--
-- Author: shunguo.chen
-- Date: 2016-06-21 20:07:44
--
-- ScrollViewExtend

local modeName = string.getModeName(...)
local ClassRef = class(modeName)

ClassRef.renderClass = nil
ClassRef.renderSprite = nil
ClassRef.renderWid = nil
ClassRef.renderHei = nil
ClassRef.space = nil
ClassRef.viewSize = nil
ClassRef.horSize = 0
ClassRef.horSpace = 0
ClassRef.upTag = nil
ClassRef.downTag = nil

ClassRef.allRenders = nil
ClassRef.preScheduleId = nil
ClassRef.curRenders = nil

ClassRef.scheduleAction = nil

ClassRef.perNum = nil
ClassRef.initPos = nil

-- 异步刷新是否完成
ClassRef._isComp = nil

function ClassRef.extend(target)
   local t = tolua.getpeer(target)
    if not t then
        t = {}
        tolua.setpeer(target, t)
    end
    setmetatable(t, ClassRef)
    return target
end

-- params = {
--      class=Render,
--      sprite=nil/node,
--      width=nil/number,
--      height=nil/number,
--      space=nil/number,
--      perNum=nil/number,
--      upTag=nil/node,
--      downTag=nil/node
-- }
function ClassRef.create(scrollView, params)
    local scrollView = ClassRef.extend(scrollView)
    scrollView:init(params)
    return scrollView
end

function ClassRef:init(params)
    self.renderClass = params.class
    self.renderSprite = params.sprite
    self.renderWid = params.width
    self.renderHei = params.height
    self.space = params.space
    self.perNum = params.perNum
    self.upTag = params.upTag
    self.downTag = params.downTag 
    self.multipleLayerNum = params.multipleLayerNum
    self.enableClone = params.enableClone
    -- 分层节点 用于 drawcall优化
    if self:isMultipleLayersItem() then
        self._mulLayerParentNode = custom.MultipleLayersParentNode:create(self.multipleLayerNum)
        self:addChild(self._mulLayerParentNode)
    end
    self.viewSize = self:getContentSize()

    self.curRenders = {}
    self.allRenders = {}

    self._isComp = true

    -- 初始化参数
    self:createRender(1)

    if self.upTag and self.downTag then
        -- print("ScrollViewExtend has up down tag")
        local function onScrollViewEvent(sender, evenType)
            if evenType == ccui.ScrollviewEventType.scrolling then
                -- print("ScrollViewExtend scrolling")
                local innerSize = self:getInnerContainerSize()

                if innerSize.height <= self.viewSize.height then
                    self.upTag:setVisible(false)
                    self.downTag:setVisible(false)
                else
                    local innerPosY = self:getInnerContainer():getPositionY()
                    self.upTag:setVisible(innerPosY < 0)
                    self.downTag:setVisible(innerPosY > (self.viewSize.height - innerSize.height))
                end
            end
        end
        self:addEventListener(onScrollViewEvent)
        self.upTag:setVisible(false)
        self.downTag:setVisible(false)
    end
end

function ClassRef:isMultipleLayersItem()
  
    return self.multipleLayerNum and self.multipleLayerNum > 0 and custom.MultipleLayersParentNode ~= nil
end

function ClassRef:createNewRender()
    local render
    if self.enableClone and self.allRenders[1] then
        render = self.allRenders[1]:clone()
        render = self.renderClass.create(render)
        if self:isMultipleLayersItem() then
            self._mulLayerParentNode:addChild(render)
        else
            self:addChild(render)
        end
    else
        if self:isMultipleLayersItem() then
            render = self.renderClass.create(self.renderSprite, true)
            self._mulLayerParentNode:addChild(render)
        else
            render = self.renderClass.create(self.renderSprite)
            self:addChild(render)
        end
    end
   
    if nil == self.renderWid then
        local width, height = render:getContentSize2()
        self.renderWid = width
        self.renderHei = height
        self.horSize = math.floor(self.viewSize.width / self.renderWid)
        self.perNum = self.perNum or self.horSize
        self.horSpace = self.viewSize.width % self.renderWid / self.horSize
        self.space = self.space or self.horSpace
        self.ap = render:getAnchorPoint()

        -- print("CreateRender self.space", self.space)
        -- print("CreateRender self.horSpace", self.horSpace)
        -- print("CreateRender self.horSize", self.horSize)
        -- print("CreateRender self.perNum", self.perNum)
        -- print("CreateRender self.ap", self.ap)
    end

   
    render:setVisible(false)
    return render
end

function ClassRef:createRender(idx)
    local render = self.allRenders[idx]
    if nil == render then
        render = self:createNewRender()
        table.insert(self.allRenders, render)
    end
    return render
end

function ClassRef:insertRender(idx)
    idx = math.min(idx, #self.allRenders + 1)
    local render = self:createNewRender()
    table.insert(self.allRenders, idx, render)
    return render
end

function ClassRef:_setInnerSize(count)
    count = count or 0
    local innerSize = cc.size(self.viewSize.width, math.ceil(count / self.horSize) * (self.renderHei + self.space))
    if innerSize.height < self.viewSize.height then
        innerSize.height = self.viewSize.height
    end

    self:setInnerContainerSize(innerSize)
    self.initPos = cc.p(self.horSpace / 2 + self.ap.x * self.renderWid,
        innerSize.height - self.space / 2 - (1-self.ap.y) * self.renderHei)
    -- dump(self.ap, "ap")
    -- dump(self.initPos, "initPos")

    if self.upTag and self.downTag then
        local innerPosY = self:getInnerContainer():getPositionY()
        self.upTag:setVisible(innerPosY < 0)
        self.downTag:setVisible(innerPosY > (self.viewSize.height - innerSize.height))
    end
end

function ClassRef:_getRenderPos(idx)
    local initPos = self.initPos or cc.p(self.renderWid/2 + self.horSpace/2, self.renderHei/2 + self.space/2)
    local x = initPos.x + ((idx - 1) % self.horSize) * (self.renderWid + self.horSpace)
    local y = initPos.y - math.floor((idx - 1) / self.horSize) * (self.renderHei + self.space)
    return cc.p(x, y)
end

-- 定位置
function ClassRef:showList(count, compFunc)
    -- print("ScrollViewExtend:showList start:"..os.time())

    -- 初始化一些状态
    self._isComp = true

    if self.scheduleAction then
        self:stopAction(self.scheduleAction)
        self.scheduleAction = nil
    end

    self.curRenders = {}
    for _, render in ipairs(self.allRenders) do
        render:setVisible(false)
    end

    if self.upTag and self.downTag then
        self.upTag:setVisible(false)
        self.downTag:setVisible(false)
    end

    -- 重置innerSize，tag
    self:_setInnerSize(count)

    -- 没有道具
    if count <= 0 then return end


    -- 有道具，开始异步刷新
    self._isComp = false

    local len = count
    local num = math.ceil(len / self.perNum)
    local times = 0
    local function onScheduler()
        times = times + 1
        local startIdx = (times - 1) * self.perNum + 1
        local endIdx = (times * self.perNum > len) and len or (times * self.perNum)
        for idx = startIdx, endIdx do
            local render = self.allRenders[idx]
            self.curRenders[idx] = render
            render:setPosition(self:_getRenderPos(idx))
            render:setVisible(true)
        end

        if endIdx == len then
            self:stopAction(self.scheduleAction)
            self.scheduleAction = nil

            self._isComp = true
            executeFunc(compFunc)
            -- print("ScrollViewExtend:showList render over:"..os.time())
        end
    end

    self.scheduleAction = self:schedule(onScheduler, 0.05)
    -- print("ScrollViewExtend:showList end:"..os.time())
end

-- 用数据刷新列表
-- datas    刷新数据
-- emptyNum 后接空白rander数量
-- onClick  render点击事件
-- compFunc 刷新全部完成回调
-- renderCompFunc 单个render刷新完成回调
function ClassRef:showDataList(datas, emptyNum, onClick, compFunc, renderCompFunc)
    -- print("ScrollViewExtend:showDataList start:"..os.time())
    -- 参数初始化
    datas = datas or {}
    emptyNum = math.max(emptyNum or 0, 0)
    local count = #datas + emptyNum
    -- print("ScrollViewExtend:showDataList count:", count)

    -- 初始化一些状态
    self._isComp = true

    if self.scheduleAction then
        self:stopAction(self.scheduleAction)
        self.scheduleAction = nil
    end

    self.curRenders = {}
    for _, render in ipairs(self.allRenders) do
        render:setVisible(false)
    end

    if self.upTag and self.downTag then
        self.upTag:setVisible(false)
        self.downTag:setVisible(false)
    end

    -- 重置innerSize
    self:_setInnerSize(count)

    -- 没有道具
    if count <= 0 then executeFunc(compFunc) return end

    -- 有道具，异步创建render
    self._isComp = false

    local len = count
    local num = math.ceil(len / self.perNum)
    local times = 0
    local function onScheduler()
        times = times + 1
        local startIdx = (times - 1) * self.perNum + 1
        local endIdx = (times * self.perNum > len) and len or (times * self.perNum)
        for idx = startIdx, endIdx do
            local render = self:createRender(idx)
            render:setData(datas[idx])
            if renderCompFunc then -- 单个render刷新完成之后逻辑
                renderCompFunc( render )
            end
            if render.setClickHandler then
                render:setClickHandler(onClick)
            elseif render.onBtnClick then
                render:onBtnClick(onClick)
            end

            self.curRenders[idx] = render
            render:setPosition(self:_getRenderPos(idx))
            render:setVisible(true)
        end

        if endIdx == len then
            if self.scheduleAction then
                self:stopAction(self.scheduleAction)
                self.scheduleAction = nil
            end
            self._isComp = true
            executeFunc(compFunc)
            -- print("ScrollViewExtend:showDataList render over:"..os.time())
        end
    end

    onScheduler()
    self.scheduleAction = self:schedule(onScheduler, 0.05)
    -- print("ScrollViewExtend:showDataList end:"..os.time())
end

-- 对visibleRenders重新设置位置
function ClassRef:_rePosVisibleRenders()
    for i,render in ipairs(self.curRenders) do
        render:setPosition(self:_getRenderPos(i))
    end
end

-- 移除idx位置的可见render
-- idx      判定方法，返回true/false
-- isKeep   是否保留，保留，需要将移除的移到最后
function ClassRef:removeVisibleByIdx(idx, isKeep)
    if idx > #self.curRenders then return end
    if idx <= 0 then return end

    local removeRender = table.remove(self.curRenders, idx)
    if removeRender then
        removeRender:setData(nil)
        if isKeep then
            table.insert(self.curRenders, removeRender)
        else
            removeRender:setVisible(false)
            self:_setInnerSize(#self.curRenders)
        end

        self:_rePosVisibleRenders()
    end
end

-- 通过外部方法来进行数据比对，找到特定的render
-- func      判定方法，返回true/false
-- isKeep    是否保留，保留，需要将移除的移到最后
function ClassRef:removeVisibleByDataFunc(func, isKeep)
    -- print("removeVisibleByDataFunc", isKeep)
    for i, render in ipairs(self.curRenders) do
        if func(render:getData()) then
            self:removeVisibleByIdx(i, isKeep)
            break
        end
    end
end

-- 插入一个Render在idx位置
function ClassRef:insertVisibleRender(idx, data, onClick)
    local visibleNum = #self.curRenders
    local render
    local extendType = nil

    if idx > visibleNum then
        extendType = 1
    else
        local render = self.curRenders[idx]
        -- 如果有数据了
        if render:getData() then
            -- 如果满了，需要扩展
            if self.curRenders[visibleNum]:getData() then
                extendType = 2
            else
                -- 把最后一个空render移到idx位置
                local removeRender = table.remove(self.curRenders, visibleNum)
                table.insert(self.curRenders, idx, removeRender)
                self:_rePosVisibleRenders()
            end
        end
    end

    if extendType then
        local newNum = visibleNum + self.perNum
        for i=visibleNum+1, newNum do
            local render = self:createRender(i)
            render:setData(nil)
            render:setVisible(true)
            self.curRenders[i] = render
        end

        if extendType == 2 then
            -- 把最后一个空render移到idx位置
            local removeRender = table.remove(self.curRenders, newNum)
            table.insert(self.curRenders, idx, removeRender)
        end

        self:_setInnerSize(newNum)
        self:_rePosVisibleRenders()
    end

    local render = self.curRenders[idx]
    render:setData(data)
    if render.setClickHandler then
        render:setClickHandler(onClick)
    elseif render.onBtnClick then
        render:onBtnClick(onClick)
    end
end

-- 在末尾push可见render
function ClassRef:pushVisibleRender(num, datas, onClick)
    datas = datas or {}
    local visibleNum = #self.curRenders
    for i=visibleNum+1, visibleNum+num do
        local render = self:createRender(i)
        render:setData(datas[i-visibleNum])
        render:setVisible(true)
        if render.setClickHandler then
            render:setClickHandler(onClick)
        elseif render.onBtnClick then
            render:onBtnClick(onClick)
        end
        self.curRenders[i] = render
    end

    self:_setInnerSize(visibleNum+num)
    self:_rePosVisibleRenders()
end

-- 通过外部方法来进行数据比对，找到并更新特定的render
function ClassRef:updateVisibleByDataFunc(func, data)
    for i, render in ipairs(self.curRenders) do
        if func(render:getData()) then
            render:setData(data)
            break
        end
    end
end

-- 容纳下count的可视数量，每次都添加 self.perNum
function ClassRef:getMaxVisibleNum(count)
    local visibleNum = #self.curRenders
    if count > visibleNum then
        local perNum = self.perNum or 1
        visibleNum = visibleNum + math.ceil((count-visibleNum)/perNum) * perNum
    end
    return visibleNum
end

function ClassRef:getVisibleRender(idx)
    return self.curRenders[idx]
end

function ClassRef:getAllRenders()
    return self.allRenders
end

function ClassRef:getVisibleRenders()
    return self.curRenders
end

function ClassRef:getVisibleNum()
    return #self.curRenders
end

function ClassRef:getAllNum()
    return #self.allRenders
end

function ClassRef:isComp()
    return self._isComp
end

function ClassRef:remove()
    if nil ~= self.allRenders then
        self.allRenders = nil
        self.curRenders = nil
        if self.scheduleAction then
            self:stopAction(self.scheduleAction)
            self.scheduleAction = nil
        end
    end
end
return ClassRef
